package 遍历器;

import 作用域相关.*;
import 作用域相关.类型;
import 抽象语法树节点.*;
import 抽象语法树节点.表达式类.成员访问表达式;
import 抽象语法树节点.表达式类.方法调用表达式;
import 抽象语法树节点.表达式类.符号引用表达式;
import 抽象语法树节点.语句类.变量定义语句;
import 抽象语法树节点.语句类.块语句;

import java.util.LinkedHashMap;
import java.util.Map;

public class 符号监听器 extends 抽象语法树基础监听器 {

    public 符号表 符号表;

    private 作用域 当前作用域;

    @Override
    public void 进入编译单元(编译单元 节点) {
        System.out.println("定义全局作用域");
        符号表 = new 符号表();
        当前作用域 = 符号表.全局作用域;
    }

    @Override
    public void 退出编译单元(编译单元 节点) {
        System.out.println("离开全局作用域");
    }

    @Override
    public void 进入结构体定义(结构体定义 节点) {
        System.out.println("定义结构体:" + 节点.名称);
        var 结构体符号 = new 结构体符号(节点.名称, 当前作用域);
        当前作用域.定义(结构体符号);

        System.out.println("进入结构体:" + 节点.名称);
        当前作用域 = 结构体符号;
    }

    @Override
    public void 退出结构体定义(结构体定义 节点) {
        var 结构体符号 = (结构体符号) 当前作用域.解析(节点.名称);
        System.out.println("离开结构体:" + 节点.名称 + ", 符号：" + 结构体符号.取符号表());
        当前作用域 = 结构体符号.取父作用域();
    }

    @Override
    public void 进入结构体成员定义(结构体成员变量定义 节点) {
        var 结构体符号 = (结构体符号) 当前作用域;
        System.out.println("解析类型:" + 节点.类型.类型名);
        var 类型 = (类型) 当前作用域.解析(节点.类型.类型名);
        System.out.println("定义成员变量:" + 节点.名称);
        结构体符号.定义(new 变量符号(节点.名称, 类型));
    }

    @Override
    public void 进入方法定义(方法定义 节点) {
        System.out.println("解析返回值类型:" + 节点.返回值类型.类型名);
        var 类型 = (类型) 当前作用域.解析(节点.返回值类型.类型名);

        System.out.println("定义方法:" + 节点.名称);
        var 方法符号 = new 方法符号(节点.名称, 类型, 当前作用域);

        System.out.println("进入方法:" + 节点.名称);
        当前作用域 = 方法符号;
    }

    @Override
    public void 退出方法定义(方法定义 节点) {
        System.out.println("离开方法:" + 节点.名称);
        当前作用域 = 当前作用域.取父作用域();
    }

    @Override
    public void 进入方法参数(方法参数 节点) {
        System.out.println("解析类型:" + 节点.类型.类型名);
        var 类型 = (类型) 当前作用域.解析(节点.类型.类型名);
        System.out.println("定义方法参数:" + 节点.名称);
        当前作用域.定义(new 变量符号(节点.名称, 类型));
    }


    @Override
    public void 进入块语句(块语句 节点) {
        System.out.println("进入局部作用域");
        当前作用域 = new 局部作用域(当前作用域);
    }

    @Override
    public void 退出块语句(块语句 节点) {
        当前作用域 = 当前作用域.取父作用域();
        System.out.println("离开局部作用域");
    }


    @Override
    public void 进入变量定义语句(变量定义语句 节点) {
        System.out.println("解析类型:" + 节点.类型.类型名);
        var 类型 = (类型) 当前作用域.解析(节点.类型.类型名);

        System.out.println("定义变量:" + 节点.名称);
        当前作用域.定义(new 变量符号(节点.名称, 类型));
    }


    @Override
    public void 进入方法调用表达式(方法调用表达式 节点) {
        System.out.println("解析方法符号:" + 节点.方法名);
        var 方法符号 = 当前作用域.解析(节点.方法名);
        if (!(方法符号 instanceof 方法符号)) {
            System.err.println("到不到方法符号:" + 节点.方法名);
        }
    }


    private final Map<成员访问表达式, 作用域> 成员访问符号表 = new LinkedHashMap<>(); // 用来存成员访问表达式计算出来的作用域

    @Override
    public void 进入成员访问表达式(成员访问表达式 节点) {

        作用域 作用域;

        // 如果为空，就在当前作用域找，不为空就取内部的作用域符号表
        if (成员访问符号表.containsKey(节点)) {
            作用域 = 成员访问符号表.get(节点);
        } else {
            作用域 = 当前作用域;
        }

        System.out.println("解析结构体符号:" + 节点.符号引用.名称);
        var 符号 = 作用域.解析(节点.符号引用.名称);
        if (符号 == null) {
            System.err.println("到不到符号:" + 节点.符号引用.名称);
            return;
        }
        
        if (!(符号.类型 instanceof 结构体符号 结构体)) {
            System.err.println("符号" + 节点.符号引用.名称 + "不是结构体类型");
            return;
        }

        // 看下右子树的类型，如果是ID，就直接解析，如果不是，就记录作用域
        if (节点.成员 instanceof 符号引用表达式) {
            var 成员符号 = 结构体.解析成员(((符号引用表达式) 节点.成员).名称);
            System.out.println("解析结构体成员符号:" + 成员符号.名称);
            if (!(成员符号 instanceof 变量符号)) {
                System.err.println("到不到成员符号:" + 节点.成员);
            }
        } else {
            成员访问符号表.put((成员访问表达式) 节点.成员, 结构体);
        }
    }
}
